import java.text.NumberFormat;

/**
 * Wrapper class for numbers in the Graphr Language
 * @author Paul Dix
 */
public class GraphrNumber extends GraphrDataType {
	//Variable declaration
	private Number number;
	
	/**
	 * Constructs a newly allocated GraphrNumber object that represents the primitive number argument.
	 * @param num - the number to be represented by the GraphrNumber.
	 */
	public GraphrNumber(Number num) {
		this.number = num;
	}
	
	/**
	 * Returns a string representation of the object.
	 * @returns a string representation of the object.
	 */
	public String toString() {
		return NumberFormat.getInstance().format(this.number);
	}
	
	/**
	 * Returns the number value of this GraphrNumber object.
	 * @returns the number value of this GraphrNumber object.
	 */
	public Number getNumber() {
		return this.number;
	}
	
	/**
	 * Returns the integer value of this GraphrNumber object.
	 * @returns the integer value of this GraphrNumber object.
	 */
	public int toInt() {
		return this.number.intValue();
	}
	
	/**
	 * Returns a new GraphrDataType representing this GraphrNumber plus rVal.<br>
	 * Will return a GraphrNumber if rVal is a GraphrNumber or GraphrString that can be converted into a number. 
	 * Otherwise will return a new GraphrString containing the concatenation of this and rVal.
	 * @param rVal - the GraphrDataType to add.
	 * @returns the result of adding this GraphrNumber to rVal.
	 */
	public GraphrDataType add(GraphrDataType rVal) {
		if (rVal.getClass() == this.getClass()) {
			return new GraphrNumber(new Double(this.getNumber().doubleValue() + ((GraphrNumber)rVal).getNumber().doubleValue()));
		}
		else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrNumber(new Double(this.getNumber().doubleValue() + r));
			} catch (NumberFormatException ex) {
				return new GraphrString(this.toString() + rVal.toString());
			}
		}
		else {
			return new GraphrString(this.toString() + rVal.toString());
		}
	}
	
	/**
	 * Returns a new GraphrNumber representing this GraphrNumber minus rVal.
	 * @param rVal - the GraphrDataType to subtract.
	 * @returns the result of subtracting rVal from this GraphrNumber.
	 */
	public GraphrDataType subtract(GraphrDataType rVal) {
		if (rVal.getClass() == this.getClass()) {
			return new GraphrNumber(new Double(this.getNumber().doubleValue() - ((GraphrNumber)rVal).getNumber().doubleValue()));
		}
		else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrNumber(new Double(this.getNumber().doubleValue() - r));
			} catch (NumberFormatException ex) {
				return new GraphrString(this.toString() + rVal.toString());
			}
		}
		else {
			return new GraphrString(this.toString() + rVal.toString());
		}
	}
	
	/**
	 * Returns a new GraphrNumber representing this GraphrNumber times rVal.
	 * @param rVal - the GraphrDataType to multiply by.
	 * @returns the result of multiplying rVal and this GraphrNumber.
	 */
	public GraphrDataType multiply(GraphrDataType rVal) {
		if (rVal.getClass() == this.getClass()) {
			return new GraphrNumber(new Double(this.getNumber().doubleValue() * ((GraphrNumber)rVal).getNumber().doubleValue()));
		}
		else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrNumber(new Double(this.getNumber().doubleValue() * r));
			} catch (NumberFormatException ex) {
				return new GraphrString(this.toString() + rVal.toString());
			}
		}
		else {
			return new GraphrString(this.toString() + rVal.toString());
		}
	}
	
	/**
	 * Returns a new GraphrNumber representing this GraphrNumber divided by rVal.
	 * @param rVal - the GraphrDataType to divide by.
	 * @returns the result of dividing this GraphrNumber by rVal.
	 */
	public GraphrDataType divide(GraphrDataType rVal) {
		if (rVal.getClass() == this.getClass()) {
			return new GraphrNumber(new Double(this.getNumber().doubleValue() / ((GraphrNumber)rVal).getNumber().doubleValue()));
		}
		else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrNumber(new Double(this.getNumber().doubleValue() / r));
			} catch (NumberFormatException ex) {
				return new GraphrString(this.toString() + rVal.toString());
			}
		}
		else {
			return new GraphrString(this.toString() + rVal.toString());
		}
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrNumber equals rVal.
	 * @param rVal - the GraphrNumber to compare with.
	 * @returns <b>true</b> if this GraphrNumber equals rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean equals(GraphrDataType rVal) {
	  if (rVal.getClass() == this.getClass()) {
  		return new GraphrBoolean(this.number.equals(((GraphrNumber)rVal).getNumber()));	    
	  }
	  else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrBoolean(this.getNumber().equals(r));
			} catch (NumberFormatException ex) {
				return new GraphrBoolean(false);
			}	    
	  }
	  else {
	    return new GraphrBoolean(false);
	  }
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrNumber is less than rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrNumber is less than rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean lessThan(GraphrDataType rVal) {
		return new GraphrBoolean(this.number.doubleValue() < ((GraphrNumber)rVal).getNumber().doubleValue());
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrNumber is greater than rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrNumber is greater than rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean greaterThan(GraphrDataType rVal) {
		return new GraphrBoolean(this.number.doubleValue() > ((GraphrNumber)rVal).getNumber().doubleValue());
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrNumber is less than or equal to rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrNumber is less than or equal to rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean lessThanEqualTo(GraphrDataType rVal) {
		return new GraphrBoolean(this.number.doubleValue() <= ((GraphrNumber)rVal).getNumber().doubleValue());
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrNumber is greater than or equal to rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrNumber is greater than or equal to rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean greaterThanEqualTo(GraphrDataType rVal) {
		return new GraphrBoolean(this.number.doubleValue() >= ((GraphrNumber)rVal).getNumber().doubleValue());
	}
  
	/**
	 * Returns a boolean representing whether this GraphrNumber equals obj.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrNumber equals obj; <b>false</b> otherwise.
	 */
/*  public boolean equals(Object obj) {
    return this.number == ((GraphrNumber)obj).getNumber();
  }
*/	
	/**
	 * Returns a GraphrDataType representing the mod of the left and right values.
	 * @param rVal - the GraphrDataType to mod with.
	 * @returns GraphrDataType if both operands are numbers, a mod operation is performed, otherwise the left hand operand is returned.
	 */
	public GraphrDataType mod(GraphrDataType rVal) {
	  if (rVal.getClass() == this.getClass()) {
	    return new GraphrNumber(this.number.doubleValue() % ((GraphrNumber)rVal).getNumber().doubleValue());
	  }
	  else if (rVal.getClass() == GraphrString.class) {
			try {
				Double r = Double.parseDouble(((GraphrString)rVal).getString());
				return new GraphrNumber(new Double(this.getNumber().doubleValue() % r));
			} catch (NumberFormatException ex) {
				return this;
			}	    
	  }
	  else {
	    return this;
	  }
	}
}